package msu.ml.util;

import java.util.*;
import java.util.regex.*;


public class CommandLineParser
{
   protected List<CommandLineOption> options;

   /*
         Syntax Example: '{d$database} $FILE [$CLASS]'

         {d$database} = optional unordered argument -d VALUE or --database=VALUE
         $FILE = required argument
         [$CLASS] = optional argument

         Argument examples:

         "-d nexrad file.txt"
         "--database=nexrad file.txt 2"
         "file.txt"

   */
   public CommandLineParser(String syntax) throws CommandLineSyntaxParseException
   {
      options = new LinkedList<CommandLineOption>();
      parseSyntax(syntax);
   }

   public void parse(String [] args) throws CommandLineParseException
   {
      Pattern shortcutPattern = Pattern.compile("-([a-zA-Z]+)");
      Pattern namePattern = Pattern.compile("--(\\w+)=(\\S+)");

      int idx = 0;
      while(idx < args.length)
      {
         Matcher shortcutMatcher = shortcutPattern.matcher(args[idx]);
         Matcher nameMatcher = namePattern.matcher(args[idx]);

         if(shortcutMatcher.matches())
         {
            String shortcut = shortcutMatcher.group(1);
            ListIterator<CommandLineOption> itr = options.listIterator();
            boolean success = false;
            while(itr.hasNext())
            {
               CommandLineOption option = itr.next();
               if(option.shortcut.equals(shortcut))
               {
                  if(option.name.length() == 0)
                  {
                     option.value = "true";
                  }
                  else if(idx + 1 >= args.length)
                  {
                     throw new CommandLineParseException("Value required for option -" + shortcut);
                  }
                  else
                  {
                     option.value = args[idx + 1];
                     idx++;
                  }
                  idx++;
                  success = true;
                  break;
               }
            }
            if(!success)
               throw new CommandLineParseException("Unknown option '-" + shortcut + "'");
         }
         else if(nameMatcher.matches())
         {
            String name = nameMatcher.group(1);
            String value = nameMatcher.group(2);

            ListIterator<CommandLineOption> itr = options.listIterator();
            boolean success = false;
            while(itr.hasNext())
            {
               CommandLineOption option = itr.next();
               if(option.name.equals(name) && (!option.ordered))
               {
                  option.value = value;
                  success = true;
                  idx++;
                  break;
               }
            }
            if(!success)
               throw new CommandLineParseException("Unknown option --"+name);

         }
         else
         {
            ListIterator<CommandLineOption> itr = options.listIterator();
            boolean success = false;
            while(itr.hasNext())
            {
               CommandLineOption option = itr.next();
               if(option.ordered && (option.value == null || option.value.equals("")))
               {
                  option.value = args[idx];
                  success = true;
                  idx++;
                  break;
               }
            }
            if(!success)
               throw new CommandLineParseException("Unknown argument "+args[idx]);
         }
      }
      ListIterator<CommandLineOption> itr = options.listIterator();
      while(itr.hasNext())
      {
         CommandLineOption option = itr.next();
         if(option.required && (option.value == null || option.value.equals("")))
         {
            throw new CommandLineParseException("Missing required argument: "+option.name);
         }
      }
   }

   public boolean hasOption(String optName)
   {
      return optionHasValue(optName);
   }

   public boolean optionHasValue(String optName)
   {
      ListIterator<CommandLineOption> itr = options.listIterator();
      while(itr.hasNext())
      {
         CommandLineOption option = itr.next();
         if(option.name.equals(optName) || option.shortcut.equals(optName))
            return !(option.value == null || option.value.equals(""));
      }
      return false;
   }

   public CommandLineOption getOption(String optName)
   {
      ListIterator<CommandLineOption> itr = options.listIterator();
      while(itr.hasNext())
      {
         CommandLineOption option = itr.next();
         if(option.name.equals(optName) || option.shortcut.equals(optName))
            return option;
      }
      return null;
   }

   public void printHelp(String [] descriptions)
   {

   }

   public void printOptions()
   {
      ListIterator<CommandLineOption> itr = options.listIterator();
      while(itr.hasNext())
      {
         CommandLineOption option = itr.next();
         System.out.println("Option: " + option.shortcut + " " + option.name);
         System.out.println("Value: " + option.value);
         System.out.println();
      }
   }

   protected void parseSyntax(String syntax) throws CommandLineSyntaxParseException
   {
      int idx = 0;
      while(idx < syntax.length())
      {
         if(syntax.charAt(idx) == '{')
         {
            int closingIdx = syntax.indexOf("}", idx + 1);
            options.add(parseUnorderedOption(syntax.substring(idx + 1, closingIdx)));
            idx = closingIdx + 1;
         }
         else if(syntax.charAt(idx) == '$')
         {
            int closingIdx = syntax.indexOf(" ", idx + 1);
            closingIdx = closingIdx == -1 ? syntax.length() : closingIdx;
            options.add(parseOrderedOption(syntax.substring(idx, closingIdx), true));
            idx = closingIdx;
         }
         else if(syntax.charAt(idx) == '[')
         {
            int closingIdx = syntax.indexOf("]", idx + 1);
            options.add(parseOrderedOption(syntax.substring(idx + 1, closingIdx), false));
            idx = closingIdx + 1;
         }
         else
         {
            idx++;
         }
      }
   }

   protected static CommandLineOption parseUnorderedOption(String input) throws CommandLineSyntaxParseException
   {
      if(input == null || input.length() == 0)
         throw new CommandLineSyntaxParseException();

      String shortcut;
      String name;

      int nameIndex = input.indexOf('$');

      if(nameIndex != -1)
      {
         shortcut = input.substring(0, nameIndex);
         name = input.substring(nameIndex+1);
      }
      else
      {
         shortcut = input;
         name = "";
      }

      CommandLineOption option = new CommandLineOption(shortcut, name);
      option.required = false;
      option.ordered = false;
      return option;
   }

   protected static CommandLineOption parseOrderedOption(String input, boolean required)
   {
      String name;
      int nameIndex = input.indexOf('$');
      name = input.substring(nameIndex+1);

      CommandLineOption option = new CommandLineOption("", name);
      option.required = required;
      option.ordered = true;
      return option;
   }
}
